using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using AI.Common;
using AI.Demo.RoomsAnalyzeTool.Models;
using AI.Demo.RoomsAnalyzeTool.Similarity;

namespace AI.Demo.RoomsAnalyzeTool.Similarity;

/// <summary>
/// Lightweight automated test harness for CrashSimilarityService.
/// Randomly samples historical records as pseudo-queries and evaluates whether
/// their own IncidentId appears in top-N (self-retrieval) as a proxy metric.
/// Output is concise JSON blocks for Copilot iterative optimization.
/// </summary>
internal static class CrashSimilarityTestHarness
{
    public sealed class EvaluationSummary
    {
        public int SampleSize { get; set; }
        public int Top1Hit { get; set; }
        public int Top3Hit { get; set; }
        public int Top5Hit { get; set; }
        public double Top1Recall => SampleSize == 0 ? 0 : Math.Round((double)Top1Hit / SampleSize, 3);
        public double Top3Recall => SampleSize == 0 ? 0 : Math.Round((double)Top3Hit / SampleSize, 3);
        public double Top5Recall => SampleSize == 0 ? 0 : Math.Round((double)Top5Hit / SampleSize, 3);
        public double AvgFirstHitRank { get; set; }
        public List<object> MissCases { get; set; } = new();
    }

    /// <summary>
    /// Executes evaluation in local mode only (avoids external AI variance) using random subset.
    /// </summary>
    public static async Task<EvaluationSummary> RunAsync(List<GSheetRecord> all, int sampleSize = 50, int seed = 2025)
    {
        var rng = new Random(seed);
        if (all == null || all.Count == 0) throw new ArgumentException("No records");
        var subset = all.Where(r => !string.IsNullOrWhiteSpace(r.CallStack) && !string.IsNullOrWhiteSpace(r.IncidentId)).ToList();
        if (subset.Count == 0) throw new ArgumentException("No usable records (empty CallStack or IncidentId)");

        sampleSize = Math.Min(sampleSize, subset.Count);
        var picked = subset.OrderBy(_ => rng.Next()).Take(sampleSize).ToList();

        // Build similarity service in forced local mode
        var dummyChat = await ChatHelper.CreateAsync(testConnection: false);
        var service = new CrashSimilarityService(dummyChat) { ForceLocal = true };
        await service.InitializeAsync(all); // full index

        var summary = new EvaluationSummary { SampleSize = sampleSize };
        int totalHitRank = 0; int hitCount = 0;

        foreach (var rec in picked)
        {
            var result = await service.QueryAsync(rec.CallStack ?? string.Empty, rec.Device ?? string.Empty, rec.ClientVersion, topN: 5);
            int rank = -1;
            if (result.Results != null)
            {
                for (int i = 0; i < result.Results.Count; i++)
                {
                    if (string.Equals(result.Results[i].IncidentId, rec.IncidentId, StringComparison.OrdinalIgnoreCase))
                    {
                        rank = i + 1; // 1-based
                        break;
                    }
                }
            }

            if (rank == 1) summary.Top1Hit++;
            if (rank > 0 && rank <= 3) summary.Top3Hit++;
            if (rank > 0 && rank <= 5) summary.Top5Hit++;
            if (rank > 0)
            {
                totalHitRank += rank;
                hitCount++;
            }
            else
            {
                summary.MissCases.Add(new
                {
                    rec.IncidentId,
                    rec.Device,
                    rec.ExceptionInfo,
                    CallStack = Truncate(rec.CallStack, 160)
                });
            }
        }

        summary.AvgFirstHitRank = hitCount == 0 ? 0 : Math.Round((double)totalHitRank / hitCount, 3);
        return summary;
    }

    private static string Truncate(string? value, int max)
    {
        if (string.IsNullOrEmpty(value)) return string.Empty;
        return value.Length <= max ? value : value.Substring(0, max) + "...";
    }

    public static string ToJson(EvaluationSummary summary)
    {
        var options = new JsonSerializerOptions
        {
            WriteIndented = true,
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
        };
        return JsonSerializer.Serialize(summary, options);
    }
}
